#ifndef H_FlawHandler
#define H_FlawHandler

/**
 * @author Conor McGann
 */

#include "MatchingRule.hh"
#include "SolverDecisionPoint.hh"
#include "ConstraintEngineListener.hh"
#include <vector>

namespace EUROPA {
namespace SOLVERS {
  
    typedef std::multimap<double, FlawHandlerId> FlawHandlerEntry;

    /**
     * @brief Factory for allocating DecisionPoint instances. Key extensions from a MatchingRule provide
     * behavior for allocation of a decision point from an entity and for storing initial configuration
     * data so that it can be accessed during actual decision point allocation.
     * @see DecisionPoint
     */
  class FlawHandler: public MatchingRule {
   public:
    virtual ~FlawHandler();

    /**
     * @brief Main factory method
     */
    virtual DecisionPointId create(const DbClientId client, const EntityId flawedEntity, const std::string& explanation) const = 0;

    /**
     * @brief Get the prority
     */
    virtual Priority getPriority(const EntityId entity = EntityId::noId());

    /**
     * @brief Retrieves a weight based on the number of criteria satisfied. Used to select the most specific
     * active flaw handler.
     */
    double getWeight() const;

    /**
     * @brief Also provide a function to refresh the weight. This permits sub class methods to be used. Call this if custom static filters
     * are used.
     */
    void refreshWeight();

    /**
     * @brief Accessor for th emax number of choices to permit.
     */
    unsigned int getMaxChoices() const;

    /**
     * @brief Tests for a match between this factory and the entity
     */
    virtual bool customStaticMatch(const EntityId entity) const;

    /**
     * @brief Tests how many costm static filters should be applied. Factors into comparative weights
     */
    virtual unsigned int customStaticFilterCount() const {return 0;}

    /**
     * @brief True if there are any guards posted on this heuristic.
     */
    bool hasGuards() const;

    /**
     * @brief Evaluates dynamic matching - i.e. guards against variables
     */
    bool test(const std::vector<ConstrainedVariableId>& scope);

    /**
     * @brief Helper method to make the scope from guard data
     * @return true if successful in obtaining all the guards required.
     */
    bool makeConstraintScope(const EntityId entity, std::vector<ConstrainedVariableId>& scope) const;

    /**
     * @brief Useful utility for eye-balling what the heuristic actually is.
     */
    virtual std::string toString() const;
 
    /**
     * @brief Handy default vector for no guards
     */
    static const std::vector< GuardEntry >& noGuards();

    /**
     * @brief Helper method to do value conversions for object domains. This one does the work.
     */
    static edouble convertValueIfNecessary(const PlanDatabaseId db,
                                           const ConstrainedVariableId guardVar,
                                           const edouble& testValue);

    static unsigned int WEIGHT_BASE() {return 100000;} /*!< Used to weight active instances. 
                                                         Establishes upper limit on priority values.*/


    friend class FlawManager;
      
    // This constraint notifies the FlawManager when a Guard on a FlawHandler is satisfied
    class VariableListener: public ConstraintEngineListener {
     public:

      /**
       * @brief Specilized constructor also provided to create from the Heuristics Engine
       */
      VariableListener(const ConstraintEngineId ce,
                       const EntityId target,
                       const FlawManagerId flawManager,
                       const FlawHandlerId flawHandler,
                       const std::vector<ConstrainedVariableId>& scope);

	
      const FlawHandlerId getHandler() const {return m_flawHandler;}
      const EntityId getTarget() const {return m_target;}
      void doWork();
      bool isApplied() const {return m_isApplied;}
      void apply();
      void undo();
      const std::vector<ConstrainedVariableId>& scope() const {return m_scope;}
     private:
      void notifyChanged(const ConstrainedVariableId variable,
			 const DomainListener::ChangeType& changeType);
      const EntityId m_target;
      const FlawManagerId m_flawManager;
      const FlawHandlerId m_flawHandler;
      bool m_isApplied;
      std::vector<ConstrainedVariableId> m_scope;
    };


   protected:

    /**
     * @brief Constructor
     * @param configData XML element of type <variable-heuristic>. May have child elements
     * particular to each derived class.
     */
    FlawHandler(const TiXmlElement& configData);

    TiXmlElement* m_configData;

    Priority m_priority; /*!< The priority for the flaw. Used in flaw ordering */
    double m_weight; /*!< An input to determine the weight of the flaw. Used to tie-break when priorities are equal */
    const std::vector< GuardEntry > m_guards; /*!< <index, value> tuples */
    const std::vector< GuardEntry > m_masterGuards; /*!< <index, value> tuples */

 

   private:
      FlawHandler(const FlawHandler&);
      FlawHandler& operator=(const FlawHandler&);

    /**
     * @brief Helper method to read the guards from XML element
     */
    static const std::vector<GuardEntry>& readGuards(const TiXmlElement& configData, bool forMaster);

    /**
     * @brief Helper method to get a double encoded value
     */
    static edouble readValue(const char* data);

    /**
     * @brief Helper method to stringify a guard
     */
    static std::string toString(const GuardEntry& entry);

    /**
    * @brief Helper method to test a guard value
    */
    bool matches(const ConstrainedVariableId guardVar, const edouble& testValue);

    /**
     * @brief Helper to get a token, if possible, from an entity (token, or variable)
     */
    static TokenId getTokenFromEntity(const EntityId entity);

    static TiXmlElement* makeConfigData(const TiXmlElement& configData);

    const PlanDatabaseId getPlanDatabase(const ConstrainedVariableId tokenVar);

    PlanDatabaseId m_db;
      
    unsigned int m_maxChoices; /*!< Allows a cut operator on choices. xml attribute is 'maxChoices' */
  }; 

    /**
     * @brief Declares a Template class for concrete decision point factories
     */
    template<class CLASS>
    class ConcreteFlawHandler: public FlawHandler{
    public:
      ConcreteFlawHandler(const TiXmlElement& config)
        : FlawHandler(config){}

      DecisionPointId create(const DbClientId client, const EntityId flaw, const std::string& explanation) const {
        DecisionPoint* dp = new CLASS(client, flaw, *FlawHandler::m_configData, explanation);
        dp->setContext(m_context);
        return dp->getId();
      }

      /**
       * @brief Tests for a match between this factory and the entity
       */
      bool customStaticMatch(const EntityId entity) const {
        return CLASS::customStaticMatch(entity);
      }

      /**
       * @brief Extends static filter count to permit a weight to be applied if custom filtering
       * is used. 
       */
      unsigned int customStaticFilterCount() const {
        return CLASS::customStaticFilterCount();
      }
    };
  }
}

#define REGISTER_FLAW_HANDLER(MGR,CLASS, NAME) \
    REGISTER_COMPONENT_FACTORY(MGR,EUROPA::SOLVERS::ConcreteFlawHandler<CLASS>,NAME)

#define REGISTER_DECISION_FACTORY(MGR,CLASS, NAME) REGISTER_FLAW_HANDLER(MGR,CLASS, NAME)

#endif
